#include <string>
#include <iostream>
#include "Value.hpp"
#include "TypeBase.hpp"

Value::Value(int i){
	this->theValue.i = i;
	this->theType = 'i';
}

Value::Value(double d){
	this->theValue.d = d;
	this->theType = 'd';
}

Value::Value(char c){
	this->theValue.c = c;
	this->theType = 'c';
}

Value::Value(bool b){
	this->theValue.b = b;
	this->theType = 'b';
}

Value::Value(std::string * s){
	this->theValue.s = s;
	this->theType = 's';
}

Value::Value() {
	this->theType = '\0';
	//ne rien faire... c'est pour NilValue et c'est très moche...
}

Value::~Value(){
}

bool Value::isOfType(Type * type){
	switch ( this->theType ) {
		case 'i': if(TypeInteger() == *type) return true; else return false;
		case 'c': if(TypeChar() == *type) return true; else return false;
		case 'd': if(TypeReal() == *type) return true; else return false;
		case 'b': if(TypeBoolean() == *type) return true; else return false;
		case 's': if(TypeString() == *type) return true; else return false;
	}

	return false;
}


bool Value::isIntervalLimit(){
	switch ( this->theType ) {
		case 'i': return true;
		case 'c':	
		case 'd':	
		case 'b':	
		case 's': return false;
	}			
	return false;
}
int Value::getInteger() {
	switch ( theType ) {
		case 'i':	return theValue.i;
		case 'd':	return theValue.d;

		default: 
		break;
	}
	return 0;
}

double Value::getReal(){
	switch ( theType ) {
		case 'i':	return theValue.i;
		case 'd':	return theValue.d;

		default: 
		break;
	}
	return 0;
}

char Value::getChar(){
	return theValue.c;
}

bool Value::getBoolean(){
	return theValue.b;
}

std::string Value::getString(){
	return *theValue.s;
}

char Value::getValType(){
	return this->theType;
}


Value * Value::clone(){ 
	switch ( this->theType ) {
		case 'i':	return new Value(theValue.i);
		case 'd':	return new Value(theValue.d);
		case 'c':	return new Value(theValue.c);
		case 'b':	return new Value(theValue.b);
		case 's':	return new Value(theValue.s);
	}			
	return NULL;

}

void Value::show() {
	switch ( this->theType ) {
		case 'i': std::cout << getInteger(); break;
		case 'c': std::cout << getChar(); break;
		case 'd': std::cout << getReal(); break;
		case 'b': std::cout << getBoolean(); break;
		case 's': std::cout << getString(); break;
	}
}


bool Value::isBoolean()
{
	if(this->theType == 'b')
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool Value::isInteger()
{
	if(this->theType == 'i')
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool Value::isReal()
{
	if(this->theType == 'd')
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool Value::isNumeric()
{
	if(this->theType == 'i' || this->theType == 'd')
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool Value::isChar()
{
	if(this->theType == 'c')
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool Value::isString()
{
	if(this->theType == 's')
	{
		return true;
	}
	else
	{
		return false;
	}
}

/* Opération unaire */
Value * Value::calculate(CodeInstruction op, Value* x)
{
	switch(op)
	{
		case OPCODE_NOT:
		{
			if(x->isBoolean())
			{
				return new Value(not(x->getBoolean()));
			}
			else
			{
				return NULL;
			}
		}
		break;
		case OPCODE_ADD:
		{
			if(x->isInteger())
			{
				return new Value(x->getInteger());
			}
			else if(x->isReal())
			{
				return new Value(x->getReal());
			}
			else
			{
				return NULL;
			}
		}
		break;
		case OPCODE_NEG:
		{
			if(x->isInteger())
			{
				return new Value((-1)*x->getInteger());
			}
			else if(x->isReal())
			{
				return new Value((-1)*x->getReal());
			}
			else
			{
				return NULL;
			}
		}
		break;
		default:
		{
			return NULL;
		}
		break;
	}
}

/* Opérations binaires */
Value * Value::calculate(CodeInstruction op, Value* x, Value* y)
{

	if(x->isNumeric() && y->isNumeric())
	{
		switch(op)
		{
			case OPCODE_ADD:
			{
				if(x->isInteger())
				{
					if(y->isInteger())
					{
						return new Value(x->getInteger()+y->getInteger());
					}
					else
					{
						return new Value(x->getInteger()+y->getReal());
					}
				}
				else
				{
					if(y->isInteger())
					{
						return new Value(x->getReal()+y->getInteger());
					}
					else
					{
						return new Value(x->getReal()+y->getReal());
					}
					
				}
			}
			break;
			case OPCODE_SUB:
			{
				if(x->isInteger())
				{
					if(y->isInteger())
					{
						return new Value(x->getInteger()-y->getInteger());
					}
					else
					{
						return new Value(x->getInteger()-y->getReal());
					}
				}
				else
				{
					if(y->isInteger())
					{
						return new Value(x->getReal()-y->getInteger());
					}
					else
					{
						return new Value(x->getReal()-y->getReal());
					}
					
				}
			}
			break;
			case OPCODE_MUL:
			{
				if(x->isInteger())
				{
					if(y->isInteger())
					{
						return new Value(x->getInteger()*y->getInteger());
					}
					else
					{
						return new Value(x->getInteger()*y->getReal());
					}
				}
				else
				{
					if(y->isInteger())
					{
						return new Value(x->getReal()*y->getInteger());
					}
					else
					{
						return new Value(x->getReal()*y->getReal());
					}
					
				}
			}
			break;
			case OPCODE_DIV:
			{
				if(x->isInteger())
				{
					if(y->isInteger() && y != 0)
					{
						return new Value(x->getInteger()/y->getInteger());
					}
					else
					{
						return new Value(x->getInteger()/y->getReal());
					}
				}
				else
				{
					if(y->isInteger())
					{
						return new Value(x->getReal()/y->getInteger());
					}
					else
					{
						return new Value(x->getReal()/y->getReal());
					}
					
				}
			}
			break;
			default:
			{
				return NULL;
			}
			break;
		}
	}
	else
	{
		if(x->isBoolean() && y->isBoolean())
		{
			switch(op)
			{
				case OPCODE_AND:
				{
					return new Value(x->getBoolean() & y->getBoolean());
				}
				break;
				case OPCODE_OR:
				{
					return new Value(x->getBoolean() | y->getBoolean());
				}
				break;
				default:
				{
					return NULL;
				}
				break;
			}
		}
		else
		{
			return NULL;
		}	
	}
}
